Skip to content

feat: recover incomplete signed application versions - #456

Merged
devantler merged 2 commits into
mainfrom
codex/actions-app-recovery-455
Oct 5, 2026
Merged

devantler merged 2 commits into
mainfrom
codex/actions-app-recovery-455

Conversation

@devantler

Copy link
Copy Markdown
Contributor

🤖 Generated by the Agentic Engineer

Why

An interrupted application release can leave its image and deployment package incomplete, while a normal retry correctly refuses to overwrite an existing version.

What

Add an opt-in recovery that authenticates the original pair and restores only missing version tags. Matching retries make no changes, and conflicting releases are refused.

Fixes #455

@github-code-quality

github-code-quality Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

Code Coverage Overview

Languages: Go, C#

Go / code-coverage/test

The overall line coverage in commit 445da55 in the codex/actions-app-re... branch remains at 50%, unchanged from commit e271b7f in the main branch.

C# / code-coverage/dotnet

The overall line coverage in commit 445da55 in the codex/actions-app-re... branch remains at 100%, unchanged from commit e271b7f in the main branch.


Updated October 04, 2026 23:57 UTC

@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

Current head: 445da550d757535440f30af1af9291cd8df29226.

RED: paired recovery was absent; the new workflow regression failed before implementation. GREEN: the actual helper passes missing/matching/partial releases, both signature failures, authentication/partial/forbidden/redirect/mixed absence reads, both conflicts, failed readbacks, interrupted writes and idempotent retries. The workflow-order simulator also executes refusal before legacy writes when either prerequisite is disabled.

Required native CI completed successfully at this head. Its native registry step observed real Cosign claim acceptance and rejection, real Flux recovery, and real Buildx recovery of a single-platform image without changing its digest. Either partial pair recovered; matching retries and every refusal produced no manifest PUTs; contradictory claims on the second signature were rejected; both latest aliases retained their original bytes.

Correctness/security and simplification self-review completed. Its two native-proof findings were fixed: count every manifest PUT and independently exercise the second signature. The local-key fixture proves cryptographic claims and registry behavior; production OIDC identity remains covered by the workflow and command contracts. Unrelated registry writers cannot be fenced by a cross-repository transaction; that limitation is documented.

@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@coderabbitai

coderabbitai Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository YAML (base), Organization UI (inherited)
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: ea2a40f3-d3de-47c3-a231-3fba7e5be56b
📥 Commits

Reviewing files that changed from the base of the PR and between e271b7f and 445da55.

📒 Files selected for processing (8)
  • .github/scripts/recover-app-version.sh
  • .github/tests/test-app-recovery.sh
  • .github/tests/test-publish-preflight.sh
  • .github/tests/test-unpublished-version-native.sh
  • .github/workflows/ci.yaml
  • .github/workflows/publish-app.yaml
  • AGENTS.md
  • README.md

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

📜 Recent review details
🧰 Additional context used
📓 Path-based instructions (2)
Source excerpt: Every `.github/tests/test-*.sh` is a test entrypoint and must have an explicit invocation in a `ci.yaml` step's `run:` block.

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • .github/tests/test-publish-preflight.sh
  • .github/tests/test-unpublished-version-native.sh
  • .github/tests/test-app-recovery.sh
Source excerpt: Repo-specific watch-list for the daily engineer: **Agent-file freshness.**

📄 CodeRabbit inference engine (AGENTS.md)

Files:

  • AGENTS.md
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: devantler-tech/.github

Timestamp: 2026-10-05T00:08:38.367Z
Learning: Source excerpt:
# AGENTS.md — devantler-tech/.github (GitHub-as-code)

## Actions catalogue

### Maintaining the catalogue

**Validate before any PR:** `actionlint` on every changed workflow/action (else a thorough YAML parse); confirm `uses:` refs resolve and are pinned/aligned; check `inputs`/`outputs`/`shell:` are declared; for reusable workflows keep `on: workflow_call` inputs/secrets backward-compatible. No app build here — YAML correctness + pinning is the gate. Keep all remote actions pinned to full-length commit SHAs; first-party self-references resolve by local path after the same-commit self-checkout (never a remote ref — see the self-reference rule above). Never weaken a security control to pass a check.
Learnt from: CR
Repo: devantler-tech/.github

Timestamp: 2026-10-05T00:08:38.367Z
Learning: Source excerpt:
# AGENTS.md — devantler-tech/.github (GitHub-as-code)

## Actions catalogue

### Adding / Changing a Reusable Workflow

#### Test jobs

Every `.github/tests/test-*.sh` is a test entrypoint and must have an explicit invocation in a
`ci.yaml` step's `run:` block. Put `bash .github/tests/test-name.sh` (or the executable path) in a
dedicated step without a step-level `if`; quoting, arguments, and surrounding comments or blank lines
are supported. Use portable filenames containing letters, digits, dots, underscores, and hyphens.
Explicit shells and job/workflow shell defaults must use `bash`; a custom shell could return
success without executing the test script.
Working-directory overrides at those scopes must be `.` so relative paths identify the repository's
actual test entrypoints, rather than a shadow script in a fixture directory.
The containing job may omit `if` or use CI's exact merge-group/release scheduling
exclusion; arbitrary job conditions do not count because th...
🪛 LanguageTool
AGENTS.md

[uncategorized] ~483-~483: The official name of this software platform is spelled with a capital “H”.
Context: ...ching retries producing no writes. Keep .github/tests/test-app-recovery.sh and the nat...

(GITHUB)

🔇 Additional comments (8)
.github/workflows/publish-app.yaml (1)

29-29: LGTM!

Also applies to: 34-34, 39-39, 44-44, 49-49, 54-54, 84-84, 174-174, 228-228, 233-233, 259-259, 270-270, 280-280, 336-336, 410-410

.github/scripts/recover-app-version.sh (1)

1-82: LGTM!

Also applies to: 85-87

.github/tests/test-app-recovery.sh (1)

1-173: LGTM!

.github/tests/test-publish-preflight.sh (1)

168-170: LGTM!

Also applies to: 510-523

.github/tests/test-unpublished-version-native.sh (1)

47-53: LGTM!

Also applies to: 80-103, 163-182, 197-313

.github/workflows/ci.yaml (1)

3259-3262: LGTM!

AGENTS.md (1)

483-484: LGTM!

README.md (1)

632-642: LGTM!


📝 Walkthrough

Walkthrough

The application publishing workflow adds a default-off recovery path that requires signed promotion and caller pinning. The recovery helper verifies both original signatures and their publication claims, reads both version aliases before writing, and restores only missing aliases from the authenticated digests. Tests cover recovery admission, registry states, interruptions, retries, and paired application artifacts. Documentation describes the recovery inputs and registry write limitations.

Priority: ➖ Normal

Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to 445da

Recovery checks both version aliases after writing. No actionable issue remains identified before merge.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 445da

Recovery is opt-in and authenticates both original artifacts before restoring version tags. Conflicting or ambiguous observations stop recovery, and latest tags are preserved. Remaining uncertainty concerns concurrent writers and production registry permissions, not a verified new vulnerability.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The configured recovery targets are the caller repository's GHCR image and its manifests repository at one normalized version. Recovery uses the publishing job's package-write credentials; downstream consumers selecting that version can observe restored aliases. Actual credential reach and package authorization remain dependent on production policy.

Security Findings and Attack Paths

  • inferred — An independently authorized writer can race recovery between alias observation and restoration. Readback can detect a resulting mismatch but cannot prevent an intervening tag from being overwritten or restore atomic pair visibility. Ordinary publication documents the same external-writer limitation; the evidence does not establish a new unauthorized attack path or a materially worsened exposure.

Trust Boundaries and Controls

  • observed — Caller-supplied recovery metadata is constrained by exact signature identity and paired annotation checks. Current source identity comes from GitHub context, while the current producer ref is resolved from the OIDC claim and required to be SHA-pinned. Both original artifacts must verify against the original publisher identity and GitHub Actions issuer before reaching tag-write operations.

Resilience and Maintainability Implications

  • observed — Recovery serializes with signed publication through the verified-publication queue and does not cancel an active publisher. Writes remain sequential: interruption can leave one restored alias, and subsequent recovery reauthenticates and reobserves the pair. Legacy publication is outside that queue.

Hardening Proposals

  • proposed — Before broader recovery adoption, establish effective package authorization and tag-overwrite policy, and fence legacy and external writers or enforce registry-side immutability. Validate concurrent-writer behavior separately from sequential recovery tests.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 12 functions across 4 files. (4 skipped: 4… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Issue #455 requires default-off recovery from the original signed artifact pair. The workflow adds five recovery inputs, gates recovery on signed promotion and caller pinning, and skips normal build, …
Out of Scope Changes check ✅ Passed The workflow, recovery helper, tests, and documentation changes support issue #455. The native registry fixture and preflight-test updates provide coverage for paired recovery and its admission requir…
Title check ✅ Passed The title clearly summarizes the main change: recovering incomplete signed application versions.
Description check ✅ Passed The description explains the interrupted-release problem and describes the opt-in recovery behavior, including restoring missing tags and refusing conflicts.
Full details: Docstring Coverage

Explanation

Docstring coverage is 8.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 12 functions across 4 files. (4 skipped: 4 unsupported.)

  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Agentic Engineer

Reviewed CodeRabbit's complete current-head review 5985957654, including its ancillary output, at 445da550d757535440f30af1af9291cd8df29226.

The 8.33% docstring warning is a generic coverage metric applied to Bash fixture helpers. These functions have no language docstring interface: their contract is expressed by descriptive names, file headers, inline parameter comments and the executable scenario assertions. The production helper documents paired authentication, admission and the non-transactional write boundary; the operator documentation supplies all recovery inputs and limitations. Inspection found no missing behavior explanation that this warning identifies, so adding artificial docstring blocks would not improve the documented contract. The language-name warning matches the literal .github/tests/test-app-recovery.sh path; capitalizing it would make the path incorrect. Both warnings are refuted at this head; body_findings=0-resolved@445da55.

The review processed all eight changed files and explicitly reported no actionable comments at this head. All 268 check contexts have settled (173 successful, 95 intentional skips), including the successful required aggregate and real registry recovery exercises. There are no review threads, no competing branch activity, and the current base is clean. The runtime evaluation is recorded in 5985878505.

@devantler
devantler marked this pull request as ready for review October 5, 2026 00:23
@devantler
devantler merged commit 4858ca0 into main Oct 5, 2026
272 checks passed
@devantler
devantler deleted the codex/actions-app-recovery-455 branch October 5, 2026 00:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Recover an incomplete application version from its original signed artifact pair

1 participant